package com.hw.nio.netty.rpc;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import jdk.internal.org.objectweb.asm.Type;
import sun.jvm.hotspot.utilities.Assert;

import javax.management.ServiceNotFoundException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class RpcServe {

    ExecutorService threadPool = Executors.newFixedThreadPool(100);
    private Map<String, ServiceBean> register = new HashMap<>();

    public void start(int port) {


        ServerBootstrap bootstrap = new ServerBootstrap();
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup work = new NioEventLoopGroup(8);
        bootstrap.group(boss, work);
        bootstrap.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {
            @Override
            protected void initChannel(NioSocketChannel ch) throws Exception {
                ch.pipeline().addLast("codec", new RpcCodec());
                ch.pipeline().addLast("dispatch", new Dispatch());
            }
        });
        bootstrap.bind(port);

    }

    private class Dispatch extends SimpleChannelInboundHandler<Transfer> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Transfer msg) throws Exception {
            /**
             * 1、处理心跳请求
             * 2、处理业务请求
             */

            if (msg.heartbeat) {
                Transfer transfer = new Transfer(msg.id);
                transfer.heartbeat = true;
                transfer.request = false;
                ctx.writeAndFlush(transfer);
            } else {
                threadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        Transfer transfer = doDispatcher(msg);
                        ctx.writeAndFlush(transfer);
                    }
                });
            }


        }

        /**
         * 1、获取请求，赋值请求ID和序列化ID
         * 2、获取服务ID，根据服务ID之星方法
         * 3、封装到响应参数中
         *
         * @param msg
         * @return
         */
        private Transfer doDispatcher(Transfer msg) {
            Request request = (Request) msg.target;
            Transfer to = new Transfer(msg.id);
            to.serializableId = msg.serializableId;
            Response response = new Response();
            try {
                String serverId = request.getClassName() + request.getMethodDesc();
                System.out.println("serverID: " + serverId);
                ServiceBean serviceBean = register.get(serverId);
                if (serviceBean == null) {
                    throw new ServiceNotFoundException("找不到服务");
                }
                Object result = serviceBean.invoke(request.getArgs());
                response.setResult(result);
            } catch (Exception e) {
                e.printStackTrace();
                response.setError(e);
                to.status = Transfer.STATUS_ERROR;
            }
            to.target = response;
            return to;
        }
    }

    public void registerServer(Class serviceInterface, Object serviceBean) {
        assert serviceInterface.isInterface();
        for (Method method : serviceInterface.getMethods()) {
            int modifiers = method.getModifiers();
            if (Modifier.isNative(modifiers) || Modifier.isStatic(modifiers)) {
                continue;
            }
            String methodDescriptor = Type.getMethodDescriptor(method);
            String key = serviceInterface.getName() + method.getName() + methodDescriptor;
            System.out.println("key:  " + key);
            register.put(key, new ServiceBean(method, serviceBean));
        }
    }

    private static class ServiceBean {
        Method method;
        Object target;

        public ServiceBean(Method method, Object target) {
            this.method = method;
            this.target = target;
        }

        public Object invoke(Object[] args) throws Exception {
            return method.invoke(target, args);
        }
    }
}
